/*
	SensorInfo - Copyright (C) 2010  Luca Minuti

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.

*/

/*
	Implementation of the main user interface classes
*/

package it.mondogufo.sensorinfo;

import java.util.*;

import android.app.Activity;
import android.app.AlertDialog;

import android.os.Bundle;
import android.os.Handler;
import android.os.Message;

import android.view.View;
import android.view.LayoutInflater;
import android.view.ViewGroup;
import android.view.Menu;
import android.view.MenuItem;
import android.view.MenuInflater;

import android.widget.Button;
import android.widget.TextView;
import android.widget.ImageView;
import android.widget.ListView;
import android.widget.LinearLayout;
import android.widget.ArrayAdapter;
import android.widget.Toast;

import android.content.Context;
import android.content.DialogInterface;
import android.content.SharedPreferences;

import android.hardware.*;

import android.util.Log;

import android.text.Spanned;
import android.text.Html;


/*
	The main activity: show all sensors and their basic informations
*/

public class SensorInfoActivity extends Activity
{
	// ID for sendEmptyMessageDelayed
	private static final int MSG_READSENSOR = 1;
	
	// Default delay between a sensor reading and another
	private static final int DEFAULT_SENSOR_DELAY = 3000;
	
	// Delay between a sensor reading and another
	private int sensorDelay = DEFAULT_SENSOR_DELAY;
	
	// True if the application must query the sensors. It's set to false when the app go to background
	private boolean autoRefresh = false;
	
	// ListView showing the sensor list
	private ListView listView;
	
	// Array with all the sensor listeners
	private ArrayList<SensorEventListener> sensorListeners = new ArrayList<SensorEventListener>();
	
	// Handler to query all sensor every n microseconds
	private Handler handler;	
		
	/*
		SensorInfoAdapter is an helper class to connect the sensor info to the view
	*/
	private class SensorInfoAdapter extends ArrayAdapter<SensorInfo> {

        private int resource;
        private LayoutInflater inflater;

        public SensorInfoAdapter( Context context, int resourceId, List<SensorInfo> objects ) {
                super( context, resourceId, objects );
                resource = resourceId;
                inflater = LayoutInflater.from( context );
        }

        @Override
        public View getView ( int position, View convertView, ViewGroup parent ) {

                SensorInfo sensorInfo = getItem( position );

                SensorInfoViewCache viewCache;

                if ( convertView == null ) {
                        convertView = ( LinearLayout ) inflater.inflate( resource, null );
                        viewCache = new SensorInfoViewCache( convertView );
                        convertView.setTag( viewCache );
                }
                else {
                        convertView = ( LinearLayout ) convertView;
                        viewCache = ( SensorInfoViewCache ) convertView.getTag();
                }

                TextView tvName = viewCache.getTextViewName();
                tvName.setText( sensorInfo.getName() );

                TextView tvVendor = viewCache.getTextViewVendor();
                tvVendor.setText( sensorInfo.getVendor() );

                TextView tvValue = viewCache.getTextViewValue();
				if (sensorInfo.getHtmlValue() != "") {
					tvValue.setText( Html.fromHtml(sensorInfo.getHtmlValue()) );
				} else {
					tvValue.setText("...");
				}

                ImageView ivType = viewCache.getImageViewType();
				switch (sensorInfo.getType())
				{
					case Sensor.TYPE_ORIENTATION: 
						ivType.setImageResource(R.drawable.orientation_icon);
						break;
					case Sensor.TYPE_ACCELEROMETER: 
						ivType.setImageResource(R.drawable.accelerometer_icon);
						break;
					case Sensor.TYPE_MAGNETIC_FIELD: 
						ivType.setImageResource(R.drawable.magfield_icon);
						break;
					case Sensor.TYPE_LIGHT: 
						ivType.setImageResource(R.drawable.light_icon);
						break;
					case Sensor.TYPE_PROXIMITY: 
						ivType.setImageResource(R.drawable.proximity_icon);
						break;
					default:  
						ivType.setImageResource(R.drawable.unknown_icon);
						break;
				}
				
                return convertView;
        }
	}	
	
	/*
		Helper class to speed up the retrivement of the TextViews
	*/
	private class SensorInfoViewCache {

        private View baseView;
        private TextView textViewName;
        private TextView textViewVendor;
        private ImageView imageViewType;
        private TextView textViewValue;

        public SensorInfoViewCache ( View baseView ) {
			this.baseView = baseView;
        }

        public TextView getTextViewName () {
			if ( textViewName == null ) {
				textViewName = ( TextView ) baseView.findViewById( R.id.sensorName );
			}
			return textViewName;
        }

        public TextView getTextViewVendor () {
			if ( textViewVendor == null ) {
				textViewVendor = ( TextView ) baseView.findViewById( R.id.sensorVendor );
			}
			return textViewVendor;
        }

        public ImageView getImageViewType () {
			if ( imageViewType == null ) {
				imageViewType = ( ImageView ) baseView.findViewById( R.id.sensorImage );
			}
			return imageViewType;
        }

        public TextView getTextViewValue () {
			if ( textViewValue == null ) {
				textViewValue = ( TextView ) baseView.findViewById( R.id.sensorValue );
			}
			return textViewValue;
        }

	}

	// Tells the adapter to refresh its contents
	private void displaySensorValue()
	{
		ArrayAdapter<SensorInfo> adapter = ( ( ArrayAdapter<SensorInfo> ) listView.getAdapter() );
		adapter.notifyDataSetChanged();
	}
		
	// Main loop to query sensor value
	private void displaySensorValueQueue()
	{
		displaySensorValue();
		if (autoRefresh)
			handler.sendEmptyMessageDelayed(MSG_READSENSOR, sensorDelay);
	}
	
	// Read sensor data and update the proper SensorInfo instance
	private void readSensorData(Sensor sensor, float[] values)
	{
		int i;
		ArrayAdapter<SensorInfo> adapter = ( ( ArrayAdapter<SensorInfo> ) listView.getAdapter() );

		for (i = 0 ; i < adapter.getCount() ; i++ ) {
			if (adapter.getItem(i).getName() == sensor.getName()) {
				adapter.getItem(i).setValues(values);
			}
		}
	}
	
	// Ask the sensor list to the device
	private void displaySensorList()
	{
		final SensorManager sensorManager = (SensorManager) this.getSystemService(Context.SENSOR_SERVICE);
		final List<Sensor> sensors = sensorManager.getSensorList(Sensor.TYPE_ALL);
		for (Sensor sensor : sensors)
		{			
			( ( ArrayAdapter<SensorInfo> ) listView.getAdapter() ).add( new SensorInfo(sensor) );

		}
		
		handler = new Handler(new Handler.Callback() {
			public boolean handleMessage(Message msg) {
				displaySensorValueQueue();
				return true;
			}
		});
	}
	
	// Ask all sensors to notify the application when some value change
	private void attachSensorEvents()
	{
		int i;
		final SensorManager sensorManager = (SensorManager) this.getSystemService(Context.SENSOR_SERVICE);
		final List<Sensor> sensors = sensorManager.getSensorList(Sensor.TYPE_ALL);
		for (i = 0 ; i < sensors.size() ; i++ )
		{
			SensorEventListener listener = new SensorEventListener() {
				public void onAccuracyChanged(Sensor sensor, int accuracy) {
				}
				public void onSensorChanged(SensorEvent event) {
					readSensorData(event.sensor, event.values);
				}
			};
			sensorListeners.add(listener);
			sensorManager.registerListener(listener, sensors.get(i), SensorManager.SENSOR_DELAY_NORMAL);
			
		}
		handler.sendEmptyMessageDelayed(MSG_READSENSOR, sensorDelay);
		//displaySensorValueQueue();
	}

	// Ask all sensors to NOT notify the application when some value change
	private void detachSensorEvents()
	{
		int i;
		final SensorManager sensorManager = (SensorManager) this.getSystemService(Context.SENSOR_SERVICE);
		final List<Sensor> sensors = sensorManager.getSensorList(Sensor.TYPE_ALL);
		for (i = 0 ; i < sensors.size() ; i++ )
		{
			sensorManager.unregisterListener(sensorListeners.get(i));
			
		}
	}
	
	// Application setting
	private void config()
	{
		final CharSequence[] items = {"500 ms", "1000 ms", "3000ms", "5000ms"};
		final int[] delays = {500, 1000, 3000, 5000};

		AlertDialog.Builder builder = new AlertDialog.Builder(this);
		builder.setTitle("Pick a delay");
		builder.setItems(items, new DialogInterface.OnClickListener() {
			public void onClick(DialogInterface dialog, int item) {
				Toast.makeText(getApplicationContext(), items[item], Toast.LENGTH_SHORT).show();
				sensorDelay = delays[item];
				saveConfig();
			}
		});
		AlertDialog alert = builder.create();
		alert.show();
	}
	
	// Open the about box
	private void about()
	{
		new AlertDialog.Builder(SensorInfoActivity.this)
			.setTitle(SensorInfoActivity.this.getString(R.string.about))
			.setMessage(SensorInfoActivity.this.getString(R.string.license_notice))
			.setNeutralButton("Ok",
				new DialogInterface.OnClickListener() {
					public void onClick(DialogInterface dialog, int which) {
					}
				}).show();
	}
	
	private void loadConfig()
	{
		SharedPreferences prefs = getPreferences(MODE_PRIVATE);
		sensorDelay = prefs.getInt("sensorDelay", DEFAULT_SENSOR_DELAY);
	}

	private void saveConfig()
	{
		SharedPreferences prefs = getPreferences(MODE_PRIVATE);
		SharedPreferences.Editor editor = prefs.edit();
		editor.putInt("sensorDelay", sensorDelay);
		editor.commit();	
	}

	// Handle menu events
	@Override
	public boolean onOptionsItemSelected(MenuItem item) {
		// Handle item selection
		switch (item.getItemId()) {
		case R.id.config:
			config();
			return true;
		case R.id.about:
			about();
			return true;
		case R.id.quit:
			finish();
			return true;
		default:
			return super.onOptionsItemSelected(item);
		}
	}	
	
	// Attach menu to the application
	@Override
	public boolean onCreateOptionsMenu(Menu menu) {
		MenuInflater inflater = getMenuInflater();
		inflater.inflate(R.menu.main, menu);
		return true;
	}	
	
	// Initialize the application
    @Override
    public void onCreate(Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
		
		loadConfig();
				
		listView = ( ListView ) findViewById( R.id.sensorListView );
		listView.setAdapter( new SensorInfoAdapter( this, R.layout.sensor, new ArrayList<SensorInfo>() ) );
		displaySensorList();
    }
	
	// Things to do when the application go foreground
    @Override
    protected void onResume()
	{
		super.onResume();
		autoRefresh = true;
		attachSensorEvents();
	}
	
	// Things to do when the application go background
    @Override
	protected void onPause()
	{
		super.onPause();
		autoRefresh = false;
		detachSensorEvents();
	}
	
}
